/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - core/system
// Copyright (c) 2014.  All Rights Reserved
//
// File:		AEDateTime.inl
// Author:		Gianluca Belardelli
// Date:		07/10/2014
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AEDATETIME_INL_
#define _AEDATETIME_INL_

#include <AECore.h>
#include <time.h>

///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
//
// AETimeSpan Methods
//
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////

AETimeSpan AETimeSpan::Infinite( void )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = ms_n64InfiniteSpan;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromDays( AEINT64 nDays )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = nDays * 24 * 60 * 60 * 1000 * 1000 * 1000;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromDays( AEDOUBLE64 dDays )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = static_cast<AEINT64>( dDays * 24.0 * 60.0 * 60.0 * 1000.0 * 1000.0 * 1000.0 );
    return tsSpan;
}

AETimeSpan AETimeSpan::FromHours( AEINT64 nHours )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = nHours * 60 * 60 * 1000 * 1000 * 1000;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromHours( AEDOUBLE64 dHours )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = static_cast<AEINT64>( dHours * 60.0 * 60.0 * 1000.0 * 1000.0 * 1000.0 );
    return tsSpan;
}

AETimeSpan AETimeSpan::FromMinutes( AEINT64 nMinutes )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = nMinutes * 60 * 1000 * 1000 * 1000;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromMinutes( AEDOUBLE64 dMinutes )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = static_cast<AEINT64>( dMinutes * 60.0 * 1000.0 * 1000.0 * 1000.0 );
    return tsSpan;
}

AETimeSpan AETimeSpan::FromSeconds( AEINT64 nSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = nSeconds * 1000 * 1000 * 1000;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromSeconds( AEDOUBLE64 dSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = static_cast<AEINT64>( dSeconds * 1000.0 * 1000.0 * 1000.0 );
    return tsSpan;
}

AETimeSpan AETimeSpan::FromMilliSeconds( AEINT64 nMilliSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = nMilliSeconds * 1000 * 1000;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromMilliSeconds( AEDOUBLE64 dMilliSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = static_cast<AEINT64>( dMilliSeconds * 1000.0 * 1000.0 );
    return tsSpan;
}

AETimeSpan AETimeSpan::FromMicroSeconds( AEINT64 nMicroSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = nMicroSeconds * 1000;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromMicroSeconds( AEDOUBLE64 dMicroSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = static_cast<AEINT64>( dMicroSeconds * 1000.0 );
    return tsSpan;
}

AETimeSpan AETimeSpan::FromNanoSeconds( AEINT64 nNanoSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = nNanoSeconds;
    return tsSpan;
}

AETimeSpan AETimeSpan::FromNanoSeconds( AEDOUBLE64 dNanoSeconds )
{
    AETimeSpan tsSpan;
    tsSpan.m_n64TotalNanoSeconds = static_cast<AEINT64>( dNanoSeconds );
    return tsSpan;
}

AEDOUBLE64 AETimeSpan::TotalDays( void ) const
{
    // Previene la trasformazione in moltiplicazione da parte dell'ottimizzatore
    static volatile AEDOUBLE64 dDivisor = 24.0 * 60.0 * 60.0 * 1000.0 * 1000.0 * 1000.0;
    return m_n64TotalNanoSeconds / dDivisor;
}

AEDOUBLE64 AETimeSpan::TotalHours( void ) const
{
    // Previene la trasformazione in moltiplicazione da parte dell'ottimizzatore
    static volatile AEDOUBLE64 dDivisor = 60.0 * 60.0 * 1000.0 * 1000.0 * 1000.0;
    return m_n64TotalNanoSeconds / dDivisor;
}

AEDOUBLE64 AETimeSpan::TotalMinutes( void ) const
{
    // Previene la trasformazione in moltiplicazione da parte dell'ottimizzatore
    static volatile AEDOUBLE64 dDivisor = 60.0 * 1000.0 * 1000.0 * 1000.0;
    return m_n64TotalNanoSeconds / dDivisor;
}

AEDOUBLE64 AETimeSpan::TotalSeconds( void ) const
{
    // Previene la trasformazione in moltiplicazione da parte dell'ottimizzatore
    static volatile AEDOUBLE64 dDivisor = 1000.0 * 1000.0 * 1000.0;
    return m_n64TotalNanoSeconds / dDivisor;
}

AEDOUBLE64 AETimeSpan::TotalMilliSeconds( void ) const
{
    // Previene la trasformazione in moltiplicazione da parte dell'ottimizzatore
    static volatile AEDOUBLE64 dDivisor = 1000.0 * 1000.0;
    return m_n64TotalNanoSeconds / dDivisor;
}

AEDOUBLE64 AETimeSpan::TotalMicroSeconds( void ) const
{
    // Previene la trasformazione in moltiplicazione da parte dell'ottimizzatore
    static volatile AEDOUBLE64 dDivisor = 1000.0;
    return m_n64TotalNanoSeconds / dDivisor;
}

AEDOUBLE64 AETimeSpan::TotalNanoSeconds( void ) const
{
    return static_cast<AEDOUBLE64>( m_n64TotalNanoSeconds );
}

AETimeSpan AETimeSpan::operator+( const AETimeSpan &tsOther ) const
{
    return AETimeSpan::FromNanoSeconds( m_n64TotalNanoSeconds + tsOther.m_n64TotalNanoSeconds );
}

AETimeSpan &AETimeSpan::operator+=( const AETimeSpan &tsOther )
{
    m_n64TotalNanoSeconds += tsOther.m_n64TotalNanoSeconds;
    return *this;
}

AETimeSpan AETimeSpan::operator-( const AETimeSpan &tsOther ) const
{
    return AETimeSpan::FromNanoSeconds( m_n64TotalNanoSeconds - tsOther.m_n64TotalNanoSeconds );
}

AETimeSpan &AETimeSpan::operator-=( const AETimeSpan &tsOther )
{
    m_n64TotalNanoSeconds -= tsOther.m_n64TotalNanoSeconds;
    return *this;
}

bool AETimeSpan::operator==( const AETimeSpan &tsOther ) const
{
    return m_n64TotalNanoSeconds == tsOther.m_n64TotalNanoSeconds;
}

bool AETimeSpan::operator!=( const AETimeSpan &tsOther ) const
{
    return m_n64TotalNanoSeconds != tsOther.m_n64TotalNanoSeconds;
}

bool AETimeSpan::operator<=( const AETimeSpan &tsOther ) const
{
    return m_n64TotalNanoSeconds <= tsOther.m_n64TotalNanoSeconds;
}

bool AETimeSpan::operator<( const AETimeSpan &tsOther ) const
{
    return m_n64TotalNanoSeconds < tsOther.m_n64TotalNanoSeconds;
}

bool AETimeSpan::operator>=( const AETimeSpan &tsOther ) const
{
    return m_n64TotalNanoSeconds >= tsOther.m_n64TotalNanoSeconds;
}

bool AETimeSpan::operator>( const AETimeSpan &tsOther ) const
{
    return m_n64TotalNanoSeconds > tsOther.m_n64TotalNanoSeconds;
}

///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
//
// AEDateTime Methods
//
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////

AEDateTime::AEDateTime( void )
{
	m_n64NanoSecondsSinceEpoch = ms_n64InvalidTime;
}

AEDateTime AEDateTime::Now( void )
{
    AETIMESTRUCT64 ttTime;
    AETIMEFUNC( &ttTime );
    return FromNative( ttTime );
}

AEDateTime AEDateTime::GetUnixEpoch( void )
{
    AEDateTime dtResult;
    dtResult.m_n64NanoSecondsSinceEpoch = 0;
    return dtResult;
}

void AEDateTime::GetDate( AETimeZone tzTimeZone, AEINT32 &nYear, AEINT32 &nMonth, AEINT32 &nDay ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    const tm* t = ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC)( &ttTime );

    nYear = t->tm_year + 1900;
    nMonth = t->tm_mon + 1;
    nDay = t->tm_mday;
}

void AEDateTime::GetTime( AETimeZone tzTimeZone, AEINT32 &nHour, AEINT32 &nMinute, AEINT32 &nSecond ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    const tm* t = ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime );

    nHour = t->tm_hour;
    nMinute = t->tm_min;
    nSecond = t->tm_sec;
}

AEINT32 AEDateTime::GetYear( AETimeZone tzTimeZone ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    return ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime )->tm_year + 1900;
}

AEINT32 AEDateTime::GetMonth( AETimeZone tzTimeZone ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    return ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime )->tm_mon + 1;
}

AEINT32 AEDateTime::GetDayOfMonth( AETimeZone tzTimeZone ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    return ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime )->tm_mday;
}

AEINT32 AEDateTime::GetHour( AETimeZone tzTimeZone ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    return ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime )->tm_hour;
}

AEINT32 AEDateTime::GetMinute( AETimeZone tzTimeZone ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    return ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime )->tm_min;
}

AEINT32 AEDateTime::GetSecond( AETimeZone tzTimeZone ) const
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    return ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime )->tm_sec;
}

AEINT32 AEDateTime::GetMilliSecond( void ) const
{
	return ( m_n64NanoSecondsSinceEpoch % ( 1000 * 1000 * 1000 ) ) / ( 1000 * 1000 );
}

AEINT32 AEDateTime::GetMicroSecond( void ) const
{
	return ( m_n64NanoSecondsSinceEpoch % ( 1000 * 1000 ) ) / 1000;
}

AEINT32 AEDateTime::GetNanoSecond( void ) const
{
	return m_n64NanoSecondsSinceEpoch % 1000;
}

AEDateTime AEDateTime::operator+( const AETimeSpan &tsSpan ) const
{
    AEDateTime dtResult;
    dtResult.m_n64NanoSecondsSinceEpoch = m_n64NanoSecondsSinceEpoch + tsSpan.m_n64TotalNanoSeconds;
    return dtResult;
}

AEDateTime &AEDateTime::operator+=( const AETimeSpan &tsSpan )
{
    m_n64NanoSecondsSinceEpoch += tsSpan.m_n64TotalNanoSeconds;
    return *this;
}

AEDateTime AEDateTime::operator-( const AETimeSpan &tsSpan ) const
{
    AEDateTime dtResult;
    dtResult.m_n64NanoSecondsSinceEpoch = m_n64NanoSecondsSinceEpoch - tsSpan.m_n64TotalNanoSeconds;
    return dtResult;
}

AEDateTime &AEDateTime::operator-=( const AETimeSpan &tsSpan )
{
    m_n64NanoSecondsSinceEpoch -= tsSpan.m_n64TotalNanoSeconds;
    return *this;
}

AETimeSpan AEDateTime::operator-( const AEDateTime &dtOther ) const
{
	return AETimeSpan::FromNanoSeconds( m_n64NanoSecondsSinceEpoch - dtOther.m_n64NanoSecondsSinceEpoch );
}

bool AEDateTime::operator==( const AEDateTime &dtOther ) const
{
	return m_n64NanoSecondsSinceEpoch == dtOther.m_n64NanoSecondsSinceEpoch;
}

bool AEDateTime::operator!=( const AEDateTime &dtOther ) const
{
	return m_n64NanoSecondsSinceEpoch != dtOther.m_n64NanoSecondsSinceEpoch;
}

bool AEDateTime::operator<=( const AEDateTime &dtOther ) const
{
	return m_n64NanoSecondsSinceEpoch <= dtOther.m_n64NanoSecondsSinceEpoch;
}

bool AEDateTime::operator<( const AEDateTime &dtOther ) const
{
	return m_n64NanoSecondsSinceEpoch < dtOther.m_n64NanoSecondsSinceEpoch;
}

bool AEDateTime::operator>=( const AEDateTime &dtOther ) const
{
	return m_n64NanoSecondsSinceEpoch >= dtOther.m_n64NanoSecondsSinceEpoch;
}

bool AEDateTime::operator>( const AEDateTime &dtOther ) const
{
	return m_n64NanoSecondsSinceEpoch > dtOther.m_n64NanoSecondsSinceEpoch;
}

bool AEDateTime::IsValid( void ) const
{
	return m_n64NanoSecondsSinceEpoch != ms_n64InvalidTime;
}

template<AEINT32 N>
AEUINT32 AEDateTime::Format( char (&buffer)[N], const char *lpFormat, AETimeZone tzTimeZone )
{
    AETIMESTRUCT64 ttTime;
    ToNative( ttTime );
    tm* t = ( tzTimeZone == UTC ? AEGMTIMEFUNC : AELOCALTIMEFUNC )( &ttTime );

    return strftime( buffer, N, lpFormat, t ) != 0 ? 1 : 0;
}

#endif // _AEDATETIME_INL_
